﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Plugin.cs" company="ScRePt">
//   Copyright © ScRePt 2010 - 2012
// </copyright>
// <summary>
//   The plugin.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace MP3D
{
    #region Using Directives

    using System;
    using System.IO;
    using System.Reflection;
    using System.Windows.Forms;
    using System.Xml.Serialization;

    using Configuration;
    using Helper;
    using MediaPortal.Configuration;
    using MediaPortal.Dialogs;
    using MediaPortal.GUI.Library;
    using MediaPortal.Player;
    using MediaPortal.Player.Subtitles;
    using MediaPortal.Profile;
    using Subtitles;

    #endregion

    /// <summary>
    /// The plugin.
    /// </summary>
    [PluginIcons(@"MP3D.Media.MP3D.png", @"MP3D.Media.MP3D_disabled.png")]
    public class Plugin : ISetupForm, IShowPlugin, IPlugin
    {
        #region Constants and Fields

        /// <summary>
        /// The current file hash.
        /// </summary>
        private string currentFileHash;

        /// <summary>
        /// The hook.
        /// </summary>
        private KeyboardHookEx hook;

        /// <summary>
        /// The mode 3d is enabled.
        /// </summary>
        private bool mode3DIsEnabled;

        /// <summary>
        /// The popup is shown.
        /// </summary>
        private bool popupIsShown;

        /// <summary>
        /// The prev window.
        /// </summary>
        private int prevWindow;

        /// <summary>
        /// The proposed mode 3d.
        /// </summary>
        private bool proposedMode3D;

        /// <summary>
        /// The settings.
        /// </summary>
        private Configuration.Configuration settings;

        /// <summary>
        /// The window.
        /// </summary>
        private MainWindow window;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="Plugin"/> class.
        /// </summary>
        public Plugin()
        {
            Log.Debug("MP3D Started");
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets a value indicating whether Mode3dIsEnabled.
        /// </summary>
        public bool Mode3DIsEnabled
        {
            get
            {
                return this.mode3DIsEnabled;
            }

            set
            {
                this.ApplyFilterToConfiguration("movieplayer", value);
                this.ApplyFilterToConfiguration("mytv", value);
                this.mode3DIsEnabled = value;
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// The main.
        /// </summary>
        [STAThread]
        public static void Main()
        {
            new Plugin().ShowPlugin();
        }

        /// <summary>
        /// The disable 3 d processing.
        /// </summary>
        public void Disable3DProcessing()
        {
            GUIDialogYesNo yn = (GUIDialogYesNo)GUIWindowManager.GetWindow((int)GUIWindow.Window.WINDOW_DIALOG_YES_NO);

            if (yn == null)
            {
                return;
            }

            this.popupIsShown = true;
            yn.SetHeading(Translations.GetByName("DlgDisable3DProcessingHeading"));
            yn.SetLine(1, Translations.GetByName("DlgDisable3DProcessingLine1"));
            yn.SetLine(2, Translations.GetByName("DlgDisable3DProcessingLine2"));
            yn.DoModal(GUIWindowManager.ActiveWindow);
            this.popupIsShown = false;

            if (!yn.IsConfirmed)
            {
                return;
            }

            if (this.window != null)
            {
                this.settings = this.window.Settings;
                this.currentFileHash = this.window.CurrentFileHash;
                this.window.DeInit();
                this.window = null;
                this.hook.IsEnabled = false;
            }

            this.Mode3DIsEnabled = false;
            this.proposedMode3D = false;
            double pos = g_Player.CurrentPosition;
            string file = g_Player.CurrentFile;
            int subtitleStream = g_Player.Player.CurrentSubtitleStream;
            bool enableSubtitles = g_Player.Player.EnableSubtitle;
            bool paused = g_Player.Player.Paused;
            g_Player.MediaType mediaType = this.GetCurrentMediaType();
            g_Player.Stop();
            this.ResumePlayer(file, mediaType);

            if (g_Player.Player != null)
            {
                g_Player.Player.EnableSubtitle = enableSubtitles;
                g_Player.Player.CurrentSubtitleStream = subtitleStream;
                this.RevertSubtitlesToOriginal();
                GUIMessage msg = new GUIMessage(GUIMessage.MessageType.GUI_MSG_SEEK_POSITION, 0, 0, 0, 0, 0, null) { Param1 = (int)pos };
                GUIGraphicsContext.SendMessage(msg);
            }

            GUIWindowManager.SendThreadMessage(new GUIMessage { Message = GUIMessage.MessageType.GUI_MSG_USER, SendToTargetWindow = true, TargetWindowId = (int)Helper.Helper.PluginWindowIds.WindowIdOsd, Param1 = 1 });

            if (paused)
            {
                // g_Player.Pause();
            }
        }

        /// <summary>
        /// The enable 3 d processing.
        /// </summary>
        public void Enable3DProcessing()
        {
            GUIDialogYesNo yn = (GUIDialogYesNo)GUIWindowManager.GetWindow((int)GUIWindow.Window.WINDOW_DIALOG_YES_NO);

            if (yn == null)
            {
                return;
            }

            this.popupIsShown = true;
            yn.SetHeading(Translations.GetByName("DlgEnable3DProcessingHeading"));
            yn.SetLine(1, Translations.GetByName("DlgEnable3DProcessingLine1"));
            yn.SetLine(2, Translations.GetByName("DlgEnable3DProcessingLine2"));
            yn.DoModal(GUIWindowManager.ActiveWindow);
            this.popupIsShown = false;

            if (!yn.IsConfirmed)
            {
                return;
            }

            if (this.window != null)
            {
                this.window.DeInit();
                this.window = null;
                this.hook.IsEnabled = false;
            }

            this.Mode3DIsEnabled = true;
            this.proposedMode3D = true;
            double pos = g_Player.CurrentPosition;
            string file = g_Player.CurrentFile;
            int subtitleStream = g_Player.Player.CurrentSubtitleStream;
            bool enableSubtitles = g_Player.Player.EnableSubtitle;
            bool paused = g_Player.Player.Paused;
            g_Player.MediaType mediaType = this.GetCurrentMediaType();
            g_Player.Stop();
            this.ResumePlayer(file, mediaType);

            if (g_Player.Player != null)
            {
                g_Player.Player.EnableSubtitle = enableSubtitles;
                g_Player.Player.CurrentSubtitleStream = subtitleStream;
                GUIMessage msg = new GUIMessage(GUIMessage.MessageType.GUI_MSG_SEEK_POSITION, 0, 0, 0, 0, 0, null) { Param1 = (int)pos };
                GUIGraphicsContext.SendMessage(msg);
            }

            GUIWindowManager.SendThreadMessage(new GUIMessage { Message = GUIMessage.MessageType.GUI_MSG_USER, TargetWindowId = (int)Helper.Helper.PluginWindowIds.WindowIdOsd, Param1 = 1 });

            if (paused)
            {
                // g_Player.Pause();
            }
        }

        #endregion

        #region Implemented Interfaces

        #region IPlugin

        /// <summary>
        /// The start.
        /// </summary>
        public void Start()
        {
            GUIWindowManager.OnActivateWindow += new GUIWindowManager.WindowActivationHandler(this.OnActivateWindow);
            g_Player.PlayBackChanged += new g_Player.ChangedHandler(this.OnPlayBackChanged);
            g_Player.PlayBackStopped += new g_Player.StoppedHandler(this.OnPlayBackChanged);
            g_Player.PlayBackEnded += new g_Player.EndedHandler(this.OnPlayBackEnded);
            GUIWindowManager.Receivers += new SendMessageHandler(this.OnGuiWindowManagerReceivers);
            this.hook = new KeyboardHookEx();
            this.hook.KeyDown += new KeyEventHandlerEx(this.OnKeyDown);
            this.hook.KeyUp += new KeyEventHandlerEx(this.OnKeyUp);

            if (!this.LoadSettings())
            {
                this.settings = new Configuration.Configuration();
                this.settings.Initialize();
            }

            this.Mode3DIsEnabled = this.settings.LoadAutomatically;
        }

        /// <summary>
        /// The stop.
        /// </summary>
        public void Stop()
        {
            if (this.window != null)
            {
                this.settings = this.window.Settings;
                this.window.DeInit();
            }

            this.window = null;
            this.hook.IsEnabled = false;
            this.hook = null;
            this.SaveSettings();
            this.Mode3DIsEnabled = false;
        }

        #endregion

        #region ISetupForm

        /// <summary>
        /// The author.
        /// </summary>
        /// <returns>
        /// The author.
        /// </returns>
        public string Author()
        {
            return Helper.Helper.PluginAuthor;
        }

        /// <summary>
        /// The can enable.
        /// </summary>
        /// <returns>
        /// The can enable.
        /// </returns>
        public bool CanEnable()
        {
            return true;
        }

        /// <summary>
        /// The default enabled.
        /// </summary>
        /// <returns>
        /// The default enabled.
        /// </returns>
        public bool DefaultEnabled()
        {
            return true;
        }

        /// <summary>
        /// The description.
        /// </summary>
        /// <returns>
        /// The description.
        /// </returns>
        public string Description()
        {
            return Helper.Helper.PluginDescription;
        }

        /// <summary>
        /// The get home.
        /// </summary>
        /// <param name="strButtonText">
        /// The str button text.
        /// </param>
        /// <param name="strButtonImage">
        /// The str button image.
        /// </param>
        /// <param name="strButtonImageFocus">
        /// The str button image focus.
        /// </param>
        /// <param name="strPictureImage">
        /// The str picture image.
        /// </param>
        /// <returns>
        /// The get home.
        /// </returns>
        public bool GetHome(out string strButtonText, out string strButtonImage, out string strButtonImageFocus, out string strPictureImage)
        {
            strButtonText = string.Empty;
            strButtonImage = string.Empty;
            strButtonImageFocus = string.Empty;
            strPictureImage = string.Empty;
            return false;
        }

        /// <summary>
        /// The get window id.
        /// </summary>
        /// <returns>
        /// The get window id.
        /// </returns>
        public int GetWindowId()
        {
            return 0;
        }

        /// <summary>
        /// The has setup.
        /// </summary>
        /// <returns>
        /// The has setup.
        /// </returns>
        public bool HasSetup()
        {
            return true;
        }

        /// <summary>
        /// The plugin name.
        /// </summary>
        /// <returns>
        /// The plugin name.
        /// </returns>
        public string PluginName()
        {
            return Helper.Helper.PluginName;
        }

        /// <summary>
        /// The show plugin.
        /// </summary>
        public void ShowPlugin()
        {
            new ConfigurationForm().ShowDialog();
        }

        #endregion

        #region IShowPlugin

        /// <summary>
        /// The show default home.
        /// </summary>
        /// <returns>
        /// The show default home.
        /// </returns>
        public bool ShowDefaultHome()
        {
            return false;
        }

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// The apply filter to configuration.
        /// </summary>
        /// <param name="section">
        /// The section.
        /// </param>
        /// <param name="value">
        /// The value.
        /// </param>
        private void ApplyFilterToConfiguration(string section, bool value)
        {
            Settings s = new MPSettings();
            int intCount;

            if (this.GetIfMode3DIsEnabled(section, out intCount, value))
            {
                return;
            }

            s.SetValueAsBool(section, "usefilter" + intCount, value);
            s.SetValue(section, "filter" + intCount, value ? "3dtv.at Stereo Transformation" : "undefined");
        }

        /// <summary>
        /// The get current media type.
        /// </summary>
        /// <returns>
        /// </returns>
        private g_Player.MediaType GetCurrentMediaType()
        {
            if (g_Player.IsVideo)
            {
                return g_Player.MediaType.Video;
            }

            if (g_Player.IsTV)
            {
                return g_Player.MediaType.TV;
            }

            if (g_Player.IsRadio)
            {
                return g_Player.MediaType.Radio;
            }

            if (g_Player.IsMusic)
            {
                return g_Player.MediaType.Music;
            }

            if (g_Player.IsTVRecording)
            {
                return g_Player.MediaType.Recording;
            }

            return g_Player.MediaType.Unknown;
        }

        /// <summary>
        /// The get mode 3d enabled.
        /// </summary>
        /// <param name="section">
        /// The section.
        /// </param>
        /// <param name="intCount">
        /// The int Count.
        /// </param>
        /// <param name="newValue">
        /// The new Value.
        /// </param>
        /// <returns>
        /// The get mode 3d enabled manually result.
        /// </returns>
        private bool GetIfMode3DIsEnabled(string section, out int intCount, bool? newValue = null)
        {
            Settings s = new MPSettings();
            intCount = 0;

            while (s.GetValueAsString(section, "filter" + intCount, "undefined") != "undefined")
            {
                string filter = s.GetValueAsString(section, "filter" + intCount, "undefined");

                if (filter == "3dtv.at Stereo Transformation")
                {
                    if (newValue.HasValue)
                    {
                        s.SetValueAsBool(section, "usefilter" + intCount, newValue.Value);
                    }

                    return s.GetValueAsBool(section, "usefilter" + intCount, false);
                }

                intCount++;
            }

            return false;
        }

        /// <summary>
        /// The load settings.
        /// </summary>
        /// <returns>
        /// The load settings result.
        /// </returns>
        private bool LoadSettings()
        {
            FileStream stream = null;

            try
            {
                XmlSerializer ser = new XmlSerializer(typeof(Configuration.Configuration));
                stream = new FileStream(Helper.Helper.ConfigFile, FileMode.Open, FileAccess.Read);
                object o = ser.Deserialize(stream);
                this.settings = (Configuration.Configuration)o;
                stream.Close();

                if (!this.settings.VerifyConfiguration())
                {
                    MainWindow.LogWarn("LoadSettings", "Settings could not be verified");
                    return false;
                }

                return true;
            }
            catch (Exception e)
            {
                if (stream != null)
                {
                    stream.Close();
                }

                Log.Warn(e.ToString());
                return false;
            }
        }

        /// <summary>
        /// The on activate window.
        /// </summary>
        /// <param name="windowId">
        /// The window id.
        /// </param>
        private void OnActivateWindow(int windowId)
        {
            if ((windowId == (int)GUIWindow.Window.WINDOW_FULLSCREEN_VIDEO || windowId == (int)GUIWindow.Window.WINDOW_TVFULLSCREEN) && ((g_Player.HasVideo && g_Player.IsVideo) || g_Player.IsTV))
            {
                this.prevWindow = GUIWindowManager.GetPreviousActiveWindow();

                if (this.window == null)
                {
                    this.window = new MainWindow { ActiveWindowId = windowId, Settings = this.settings, ParentPlugin = this, IsMode3D = this.proposedMode3D, CurrentFileHash = this.currentFileHash };

                    if (!this.window.Init())
                    {
                        this.window.DeInit();
                        this.window = null;
                        return;
                    }

                    this.proposedMode3D = false;
                }
                else
                {
                    this.window.Refresh(false); // Do not reinitialize 3D window and values
                }

                this.hook.IsEnabled = false;
                this.hook.IsEnabled = true;
            }
            else if (!(g_Player.HasVideo || g_Player.IsTV) || windowId == this.prevWindow || windowId == (int)GUIWindow.Window.WINDOW_HOME || !g_Player.FullScreen)
            {
                this.currentFileHash = string.Empty;

                if (this.window != null)
                {
                    this.settings = this.window.Settings;
                    this.window.DeInit();
                    this.window = null;
                }

                this.hook.IsEnabled = false;
            }
        }

        /// <summary>
        /// The on gui window manager receivers.
        /// </summary>
        /// <param name="message">
        /// The message.
        /// </param>
        private void OnGuiWindowManagerReceivers(GUIMessage message)
        {
            if (message.TargetWindowId == (int)Helper.Helper.PluginWindowIds.WindowIdOsd && message.Message == GUIMessage.MessageType.GUI_MSG_USER)
            {
                switch (message.Param1)
                {
                    case 0:
                        if (this.window != null)
                        {
                            this.window.ExecuteCommand((MenuCommand)message.Object);
                        }

                        break;
                    case 1:
                        this.OnActivateWindow(GUIWindowManager.ActiveWindow);
                        break;
                    default:
                        break;
                }
            }
        }

        /// <summary>
        /// The on key down.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnKeyDown(object sender, ref KeyEventArgs e)
        {
            if (!GUIGraphicsContext.HasFocus || this.popupIsShown)
            {
                return;
            }

            if (this.window != null && this.window.WindowLoaded)
            {
                this.window.OnKeyDown(sender, ref e);
            }
        }

        /// <summary>
        /// The on key up.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        /// <param name="e">
        /// The e.
        /// </param>
        private void OnKeyUp(object sender, ref KeyEventArgs e)
        {
            if (!GUIGraphicsContext.HasFocus || this.popupIsShown)
            {
                return;
            }

            if (this.window != null && this.window.WindowLoaded)
            {
                this.window.OnKeyUp(sender, ref e);
            }
        }

        /// <summary>
        /// The on playback changed.
        /// </summary>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <param name="stoptime">
        /// The stoptime.
        /// </param>
        /// <param name="filename">
        /// The filename.
        /// </param>
        private void OnPlayBackChanged(g_Player.MediaType type, int stoptime, string filename)
        {
            if (this.window != null)
            {
                this.settings = this.window.Settings;
                this.window.DeInit();
            }

            this.currentFileHash = string.Empty;
            this.window = null; // Reinitialize 3D window and values
            this.hook.IsEnabled = false;
        }

        /// <summary>
        /// The on play back ended.
        /// </summary>
        /// <param name="type">
        /// The type.
        /// </param>
        /// <param name="filename">
        /// The filename.
        /// </param>
        private void OnPlayBackEnded(g_Player.MediaType type, string filename)
        {
            this.OnPlayBackChanged(type, 0, filename);
        }

        /// <summary>
        /// The resume player.
        /// </summary>
        /// <param name="file">
        /// The file.
        /// </param>
        /// <param name="mediaType">
        /// The media type.
        /// </param>
        private void ResumePlayer(string file, g_Player.MediaType mediaType)
        {
            if (mediaType == g_Player.MediaType.TV)
            {
                Type tvtype = Type.GetType("TvPlugin.TVHome, TvPlugin");

                if (tvtype != null)
                {
                    PropertyInfo navigatorType = tvtype.GetProperty("Navigator", BindingFlags.Static | BindingFlags.Public);

                    if (navigatorType != null)
                    {
                        object navigator = navigatorType.GetValue(null, null);

                        if (navigator != null)
                        {
                            PropertyInfo channelType = navigator.GetType().GetProperty("Channel");

                            if (channelType != null)
                            {
                                object channel = channelType.GetValue(navigator, null);

                                if (channel != null)
                                {
                                    MethodInfo viewchannelType = tvtype.GetMethod("ViewChannel", BindingFlags.Static | BindingFlags.Public);

                                    if (viewchannelType != null)
                                    {
                                        viewchannelType.Invoke(null, new[] { channel });
                                    }
                                }
                            }
                        }
                    }
                }
            }
            else
            {
                g_Player.Play(file, mediaType);
            }
        }

        /// <summary>
        /// The revert subtitles to original.
        /// </summary>
        private void RevertSubtitlesToOriginal()
        {
            if (SubEngine.engine is StereoSubEngine)
            {
                SubEngine.engine = ((StereoSubEngine)SubEngine.engine).BaseEngine;
            }
        }

        /// <summary>
        /// The save settings.
        /// </summary>
        private void SaveSettings()
        {
            if (this.settings == null)
            {
                return;
            }

            XmlSerializer ser = new XmlSerializer(typeof(Configuration.Configuration));
            FileStream stream = new FileStream(Helper.Helper.ConfigFile, FileMode.Create, FileAccess.Write);
            ser.Serialize(stream, this.settings);
            stream.Close();
        }

        #endregion
    }
}